package com.blog.wl2023.aspect;

import com.blog.wl2023.entity.response.PageResult;
import com.blog.wl2023.entity.response.Result;
import com.blog.wl2023.utils.note.ResponseResult;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * @author wule
 * @data 2023年08月15日18时
 * @description 全局响应数据预处理器, 使用RestControllerAdvice和ResponseBodyAdvice, 拦截Controller方法默认返回参数，统一处理响应体
 *                 :主要就是判断是否为HandlerMethod，然后判断是否有标注了我们自定义的注解ResponseResult，有的话就在request的属性里设置RESPONSE_RESULT_ATTR
 */
@RestControllerAdvice
public class RestResponseHandler implements ResponseBodyAdvice<Object> {
	// 属性名称，用于记录是否标注了ResponseResult注解
	public static final String RESPONSE_RESULT_ATTR = "RESPONSE_RESULT_ATTR";

	@Autowired
	private ObjectMapper objectMapper;

	/**
	 * 判断是否需要执行beforeBodyWrite方法，true为执行；false为不执行
	 *
	 * @param returnType
	 * @param converterType
	 */
	@Override
	public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
		ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = requestAttributes.getRequest();

		// 判断请求是否有注解标记
		ResponseResult responseResultAnn = (ResponseResult) request.getAttribute(RESPONSE_RESULT_ATTR);
		return responseResultAnn != null;
	}

	/**
	 * 对返回值包装处理
	 *
	 * @param body
	 * @param returnType
	 * @param selectedContentType
	 * @param selectedConverterType
	 * @param request
	 * @param response
	 */
	@SneakyThrows
	@Override
	public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {

		// 已经返回的是Result类型对象了，直接返回，比如全局异常处理之后直接返回了
		if (body instanceof Result || body instanceof PageResult) {
			return body;
		}
		else if (body instanceof String) { // 如果Controller直接返回String时，需要转换为Json，因为强化的是RestController
			return objectMapper.writeValueAsString(Result.ok(body));
		}
		return Result.ok(body);
	}
}

